package pl.wwysocki.gendok;

import java.lang.reflect.Method;
import java.lang.reflect.Type;
import java.util.HashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.odftoolkit.odfdom.doc.text.OdfTextUserFieldGet;
import org.w3c.dom.Node;

public class RelationRef extends FieldRef
{
	private static final Log log = LogFactory.getLog(RelationRef.class);
	private HashMap<Method, FieldRef> fields = new HashMap<Method, FieldRef>();

	public RelationRef()
	{
	}

	@Override
	public Object getIdentifier()
	{
		return this;
	}

	public RelationRef(String name, Node element, Method getter)
	{
		super(name, element, getter);
	}
	
	public void generateTables(Object obj)
	{
		Object relBean = null;
		
		try
		{
			if (getGetter() != null && obj != null)
			{
				relBean = getGetter().invoke(obj, null);
			}
			else
			{
				log.error(String.format("Brak gettera %s lub obiektu %s.", getGetter(), obj));
			}
		}
		catch (Exception e)
		{
			log.error(String.format("Blad wywolania metody %s na rzecz obiektu %s", getGetter(), obj));
			e.printStackTrace();
		}
		if (relBean != null)
		{
			for (FieldRef fieldRef : fields.values())
			{
				log.info(String.format("%s.generateTables(%s) -> %s.generateTables(...)", getName(), relBean, fieldRef));
				fieldRef.generateTables(relBean);
			}
		}
	}
	
	public void removeTableRow()
	{
		for (FieldRef fieldRef : fields.values())
		{
			log.info(String.format("%s.removeTableRow() -> %s.removeTableRow()", getName(), fieldRef));
			fieldRef.removeTableRow();
		}
	}

	public void resetTable()
	{
		for (FieldRef fieldRef : fields.values())
		{
			log.info(String.format("%s.resetTable() -> %s.resetTable()", getName(), fieldRef));
			fieldRef.removeTableRow();
		}
	}

	public void parse(OdfTextUserFieldGet element, String name, Class<?> c)
	{
		int dot = name.indexOf('.');
		if (dot >= 0)
		{
			String objName = name.substring(0, dot);
			String attrName = name.substring(dot + 1);
			Method getter = null;
			try
			{
				getter = c.getDeclaredMethod("get" + objName, null);
			}
			catch (Exception e)
			{
				log.error(String.format("Blad %s gettera get%s w klasie %s.", objName, c.getName(), e.getMessage()));
				e.printStackTrace();
			}
			Type type = getter.getGenericReturnType();
			String typeName = type.toString();
			if (typeName.startsWith("java.util.Set"))
			{
				log.info(String.format("Proba obslugi relacji jeden do wiele wewnatrz relacji wiele do jednego"));
				String className = typeName.substring(14, typeName.length() - 1);
				Object bean1 = null;
				try
				{
					c = Class.forName(className);
					bean1 = c.newInstance();
				}
				catch (Exception e)
				{
					log.error(String.format("Blad utworzenia obiektu klasy %s", className));
					e.printStackTrace();
				}
				FieldRef collRef = null;
				if (fields.containsKey(getter))
				{
					collRef = fields.get(getter);
				}
				else
				{
					collRef = new CollRef(objName, element, getter);
					addField(getter, collRef);
				}
				collRef.parse(element, attrName, bean1);
			} // Set
			else
			// Przypadek, kiedy pole z relacji jeden/wiele do jednego
			{
				Class<?> c1 = null;
				String className = typeName.substring(6);
				try
				{
					c1 = Class.forName(className);
				}
				catch (Exception e)
				{
					log.error(String.format("Blad utworzenia obiektu klasy %s", className));
					e.printStackTrace();
				}
				RelationRef relRef = null;
				if (fields.containsKey(getter))
				{
					relRef = (RelationRef) fields.get(getter);
				}
				else
				{
					relRef = new RelationRef(objName, element, getter);
					addField(getter, relRef);
				}
				relRef.parse(element, attrName, c1);
				
			}
		} // dot >= 0
		else
		{
			Method getter = null;
			try
			{
				getter = c.getDeclaredMethod("get" + name, null);
			}
			catch (Exception e)
			{
				log.error(String.format("Blad wyszukania gettera get%s w klasie %s.", name, c.getName()));
			}
			FieldRef fieldRef = null;
			if (fields.containsKey(getter))
			{
				fieldRef = fields.get(getter);
			}
			else
			{
				fieldRef = new FieldRef(name, element, getter);
				addField(getter, fieldRef);
			}
		}
	}

	private void addField(Method getter, FieldRef collRef)
	{
		fields.put(getter, collRef);
	}

	public void show()
	{
		log.info("RelationRef");
		log.info(String.format("relacja: %s", getName()));
		log.info(String.format("getter: %s", getGetter()));
		for (FieldRef fieldRef : fields.values())
		{
			fieldRef.show();
		}
		log.info("------------------------");
	}
}
